package main

import (
	"fmt"
	"os"
	"net"
	"strings"

	"github.com/vishvananda/netlink"
	"github.com/docker/go-plugins-helpers/network"
	
)


const (
	PluginDataDir   = "/var/lib/docker-pipe-network/metadata/"
)

type bridgeDriver struct {
	name 			string
	networks 	    map[string]*bridgeConfiguration
	endPoints 		map[string]*endPoint
}

type bridgeConfiguration struct {
	name	  	string
	mtu	        int
	address  	*net.IPNet
	gateway   	string
}

type endPoint struct {
	id 			string
	nid 		string
	srcName 	string
	address 	*net.IPNet
}

// parase gateway and mask
func (bridge *bridgeConfiguration) getGatewayIP(request *network.CreateNetworkRequest) (error) {
	var err error
	if _, bridge.address, err = net.ParseCIDR(request.IPv4Data[0].Pool); err !=nil {
		return err
	}

	bridge.gateway = request.IPv4Data[0].Gateway
	return nil
}


func NewbridgeDriver() *bridgeDriver {
	fmt.Println("Starting... ")
	os.Mkdir(PluginDataDir, 0700)
	driver := initialCache()

	return &driver
}

func (driver *bridgeDriver) GetCapabilities() (*network.CapabilitiesResponse, error) {
	return &network.CapabilitiesResponse{Scope: "local"}, nil
}
// create network method
func (driver *bridgeDriver) CreateNetwork(createNetworkRequest *network.CreateNetworkRequest) error {
	fmt.Println("create network... ")

	// get ip pool and gateway info
	for _, ip := range createNetworkRequest.IPv4Data {
		fmt.Printf("request IPAM Data Address Space %s Pool %s Gateway %s\n", ip.AddressSpace, ip.Pool, ip.Gateway)
	}

	// get network id
	networkId := createNetworkRequest.NetworkID

	// if _, ok := driver.networks[networkId]; !ok {
	// 	fmt.Errorf("network %s not exists", networkId)
	// }

	// init bridgeConfiguration
	brigeConf := bridgeConfiguration{
		name:		  "gbr-" + networkId[:10],
		mtu:		  1500,	   
	}
	if err:= brigeConf.getGatewayIP(createNetworkRequest); err != nil {
		return err
	}
	// Set value for variable driver.networks
	driver.networks[networkId] = &brigeConf
	// create linux brige
	if err := driver.createBridge(networkId); err != nil {
		fmt.Println(err)
		return err
	}
	return nil
}
func (driver *bridgeDriver) DeleteNetwork(deleteNetworkRequest *network.DeleteNetworkRequest) error {
	fmt.Println("DeleteNetwork")
	return nil
}
func (driver *bridgeDriver) CreateEndpoint(createEndpointRequest *network.CreateEndpointRequest) (*network.CreateEndpointResponse, error) {
	fmt.Println("CreateEndpoint")
	response := &network.CreateEndpointResponse{}
	endPointId := createEndpointRequest.EndpointID
	ipaddress := createEndpointRequest.Interface.Address
	fmt.Println("network=%s",ipaddress)
	veth := "veth" + endPointId[:5]
	peer := "peer" + endPointId[:5]

	// init endPoint
	end := endPoint{
		id: 			endPointId,
		nid: 			createEndpointRequest.NetworkID,
		srcName:		veth,
	}
	driver.endPoints[endPointId] = &end
	vethPeer := &netlink.Veth{
		LinkAttrs: netlink.LinkAttrs{Name: peer, TxQLen: 0},
		PeerName:  veth,
	}
	if err := netlink.LinkAdd(vethPeer); err != nil {
		return response, fmt.Errorf("[CreateEndpoint] failed to add the veth device")
	}

	hostside, err := netlink.LinkByName(peer)
	if err != nil {
		return response, fmt.Errorf("[CreateEndpoint] failed to get host side interface")
	}
	defer func() {
		if err != nil {
			netlink.LinkDel(hostside)
		}
	}()
	containerside, err := netlink.LinkByName(veth)
	if err != nil {
		return response, fmt.Errorf("[CreateEndpoint] failed to get container side interface")
	}
	defer func() {
		if err != nil {
			netlink.LinkDel(containerside)
		}
	}()

	if err := netlink.LinkSetUp(containerside); err != nil {
		return response, fmt.Errorf("[setup] failed to set link up for %s", containerside)
	}

	if err := netlink.LinkSetUp(hostside); err != nil {
		return response, fmt.Errorf("[setup] failed to set link up for %s", hostside)
	}

	master, err := netlink.LinkByName(driver.networks[createEndpointRequest.NetworkID].name) 
	if err != nil {
		return response, fmt.Errorf("[Get] failed to get master Bridge.")
	}

	if err = netlink.LinkSetMaster(hostside, master); err != nil {
		return response, fmt.Errorf("[CreateEndpoint] failed to add hostside to Bridge")
	}

	return response, nil
}
func (driver *bridgeDriver) DeleteEndpoint(deleteEndpointRequest *network.DeleteEndpointRequest) error {
	fmt.Println("DeleteEndpoint...")
	eid := deleteEndpointRequest.EndpointID

	ep, ok := driver.endPoints[eid]
	if !ok {
		return fmt.Errorf("[DeleteEndpoint] Endpoint %s not found", eid)
	}

	link, err := netlink.LinkByName(ep.srcName)
	if err != nil {
		return fmt.Errorf("[DeleteEndpoint] failed to get interface %s", ep.srcName)
	}

	err = netlink.LinkDel(link)
	if err != nil {
		return fmt.Errorf("[DeleteEndpoint] failed to delete interface %s", ep.srcName)
	}

	delete(driver.endPoints, eid)
	return nil
}
func (driver *bridgeDriver) EndpointInfo(infoRequest *network.InfoRequest) (*network.InfoResponse, error) {
	fmt.Println("endpoint info...")

	return nil, nil
}
func (driver *bridgeDriver) AllocateNetwork(infoRequest *network.AllocateNetworkRequest) (*network.AllocateNetworkResponse, error) {

	fmt.Println("AllocateNetwork...")
	return nil, nil
}
func (driver *bridgeDriver) FreeNetwork(infoRequest *network.FreeNetworkRequest) error {
	fmt.Println("FreeNetwork...")
	return nil

}
func (driver *bridgeDriver) Join(joinRequest *network.JoinRequest) (*network.JoinResponse, error) {
	fmt.Println("joing....")
	eid := joinRequest.EndpointID

	resp := &network.JoinResponse{}
	resp.InterfaceName.SrcName = driver.endPoints[eid].srcName
	resp.InterfaceName.DstPrefix = "eth"
	parts := strings.Split(driver.networks[joinRequest.NetworkID].gateway, "/")
	if parts[0] == "" || parts[1] == "" {
		return nil, fmt.Errorf("Cannot split gateway IP address")
	}
	resp.Gateway = parts[0]
	return resp, nil
}
func (driver *bridgeDriver) Leave(leaveRequest *network.LeaveRequest) error {
	fmt.Println("Leave...")
	return nil
}
func (driver *bridgeDriver) DiscoverNew(discoveryNotification *network.DiscoveryNotification) error {
	fmt.Println("DiscoverNew...")
	return nil
}
func (driver *bridgeDriver) DiscoverDelete(discoveryNotification *network.DiscoveryNotification) error {
	fmt.Println("DiscoverDelete...")
	return nil
}
func (driver *bridgeDriver) ProgramExternalConnectivity(programExternalConnectivityRequest *network.ProgramExternalConnectivityRequest) error {
	fmt.Println("ProgramExternalConnectivity...")

	return nil
}
func (driver *bridgeDriver) RevokeExternalConnectivity(revokeExternalConnectivityRequest *network.RevokeExternalConnectivityRequest) error {
	fmt.Println("RevokeExternalConnectivity...")
	return nil
}
func initialCache() bridgeDriver {
	driver := bridgeDriver{
		name: "gbridge",
		networks: make(map[string]*bridgeConfiguration),
		endPoints: make(map[string]*endPoint),
	}
	return driver
}

// create and set up linux bridge 
func (driver *bridgeDriver) createBridge(networkId string) error {
	fmt.Println(networkId)
	endPoint := &netlink.Bridge{
			LinkAttrs: netlink.LinkAttrs{
				Name: driver.networks[networkId].name,
			},
	}
	if err := netlink.LinkAdd(endPoint); err != nil {
		fmt.Println(err)
		return fmt.Errorf("[setup] failed to create bridge %s",  driver.networks[networkId].name)
	}
	if err := netlink.LinkSetUp(endPoint); err != nil {
		return fmt.Errorf("[setup] failed to set link up for %s", driver.networks[networkId].name)
	}
	fmt.Println(driver.networks[networkId].gateway)
	i, n, _ := net.ParseCIDR(driver.networks[networkId].gateway)
    addr := &net.IPNet{IP: i, Mask: n.Mask}
    nlAddr := &netlink.Addr{IPNet: addr, Label: ""}
	if err := netlink.AddrAdd(endPoint, nlAddr); err != nil {
		return fmt.Errorf("[setup] failed to set link up for %s", driver.networks[networkId].name)
	}
	return nil
}



